# This file is part of OrangeSpider. OrangeSpider is a meta model based
# development tool.
# 
# OrangeSpider is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# OrangeSpider is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with OrangeSpider.  If not, see <http://www.gnu.org/licenses/>.
#
# Copyright 2009 Stefan Nikolaus <OrangeSpiderMaster@googlemail.com>

from MetaModel import *
import Ui
import FastEdit
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import Component
import math
import random
import Persistence


def makeColor(color):
    c = QColor(color[0], color[1], color[2], color[3])
    return c

def pixmapLoader(file):
    bytes = QByteArray()
    buffer = QBuffer(bytes)
    buffer.open(QIODevice.WriteOnly)
    pixmap = QPixmap(file)
    pixmap.save(buffer, "PNG")
    s = str(bytes.data())
    return s

def asQSizeF(qsize):
    return QSizeF(qsize.width(), qsize.height())

class Edge(QGraphicsLineItem):
    Type = QGraphicsItem.UserType + 2
    def __init__(self, repObject, edgeDef, myStartItem, myDestItem):
        QGraphicsLineItem.__init__(self)
        self.edgeDef = edgeDef
        self.myStartItem = myStartItem
        self.myDestItem = myDestItem
        self.setFlag(QGraphicsItem.ItemIsSelectable, 1)
        self.arrowHead = QPolygonF()
        self.myColor = Qt.black
        self.setPen(QPen(self.myColor, 2, Qt.SolidLine, 
                         Qt.RoundCap, Qt.RoundJoin))
        self.repObject = repObject

    def boundingRect(self):
        #if not self.myStartItem or not self.myDestItem:
        #    return QRectF()
        extra = (self.pen().width() + 20) / 2.0
        rect = QRectF(self.line().p1(), 
               QSizeF(self.line().p2().x() - self.line().p1().x(),
                      self.line().p2().y() - self.line().p1().y()))
        return rect.normalized().adjusted(-extra, -extra, extra, extra)

    def shape(self):
         path = QGraphicsLineItem.shape(self)
         path.addPolygon(self.arrowHead)
         return path

    def type(self):
        return Edge.Type

    def updatePosition(self):
        line = QLineF(self.mapFromItem(self.myStartItem, 40, 0), 
                      self.mapFromItem(self.myDestItem, 0, 0))
        self.setLine(line)

    def paint(self, painter, options, widget):
       if self.myStartItem.collidesWithItem(self.myDestItem):
          return
       myPen = self.pen()
       arrowSize = 13.0
       painter.setPen(myPen)
       myPen.setColor(Qt.black)
       painter.setBrush(self.myColor)

       centerLine = QLineF(self.myStartItem.pos(), 
                           self.myDestItem.pos())
       endPolygon = QPolygonF(self.myDestItem.rect())
       p1 = QPointF(endPolygon.first() + self.myDestItem.pos())
       intersectPoint = QPointF()
       polyLine = QLineF()
       for i in range(1, endPolygon.count()):
          p2 = endPolygon.at(i) + self.myDestItem.pos()
          polyLine = QLineF(p1, p2)
          intersectType = polyLine.intersect(centerLine, intersectPoint)
          if intersectType == QLineF.BoundedIntersection:
               break
          p1 = p2
    
       self.setLine(QLineF(intersectPoint, self.myStartItem.pos()))
       angle = 0
       if self.line().length() != 0:
           angle = math.acos(self.line().dx() / self.line().length())
       if self.line().dy() >= 0:
          angle = (math.pi * 2) - angle

       arrowP1 = QPointF(self.line().p1() 
                 + QPointF(math.sin(angle + math.pi / 2.5) * arrowSize,
                 math.cos(angle + math.pi / 2.5) * arrowSize))
       arrowP2 = QPointF(self.line().p1() 
                 + QPointF(math.sin(angle + math.pi - math.pi / 2.5) * arrowSize,
                 math.cos(angle + math.pi - math.pi / 2.5) * arrowSize))

       self.arrowHead.clear()
       self.arrowHead.append(self.line().p1())
       self.arrowHead.append(arrowP1)
       self.arrowHead.append(arrowP2)
       painter.drawLine(self.line())
       painter.drawPolygon(self.arrowHead)
       if self.isSelected():
           painter.setPen(QPen(self.myColor, 1, Qt.DashLine))
           myLine = QLineF(self.line())
           myLine.translate(0, 4.0)
           painter.drawLine(myLine)
           myLine.translate(0,-8.0)
           painter.drawLine(myLine)


class Evaluator:
    def __init__(self, item):
        for mapping in item.shapeDesc.eachOutgoingClass("Shape.FilledBy.Mapping"):
            if mapping["Property"] in item.supplyingProperties():
                data = item.data
                expr = mapping["Expression"]
                value = eval(expr)
                item.setProperty(mapping["Property"], value)
            else:
                print "Unkown property " + mapping["Property"]


class Drawer:
    def __init__(self, parent):
        self.parent = parent
        self.bestSize = QSizeF(0,0)
        self.props = {
            "BackgroundColor": (255, 255, 255, 255),
            "ForegroundColor": (0, 0, 0, 255),
            "BorderColor": (0, 0, 0, 255),
            "PenWidth": 1,
            "Picture": "",
            "Text": "",
            "Font": "Arial",
            "FontSize": 10
        }
    def build(self, shapeDesc, data, factory, indent = 0):
        self.shapeDesc = shapeDesc
        self.data = data
        #print " " * indent, shapeDesc["Name"], shapeDesc["Id"], data["Name"], data["Id"]

        # First copy the shape desc property values over the defaults
        for mp in self.shapeDesc.intention.eachMetaProperty():
            propName = mp["Name"]
            if propName in self.props:
                self.props[propName] = self.shapeDesc[propName]

        # .. then maybe change the values according to a mapping
        # Calculate properties for this item
        e = Evaluator(self)

    def desiredSize(self):
        pass
    def draw(self, pos, painter, indent = 0):
        pass
    def supplyingProperties(self):
        return self.props.keys()
    def setProperty(self, name, value):
        self.props[name] = value
            
    def _makePen(self):
        p = QPen(makeColor(self.props["ForegroundColor"]))
        p.setWidth(int(self.props["PenWidth"]))
        #p.setPenStyle ...
        return p
    
    def _makeBrush(self):
        b = QBrush(makeColor(self.props["BackgroundColor"]))
        return b
    
class TextDrawer(Drawer):
    def __init__(self, parent):
        Drawer.__init__(self, parent)
        self.props["Text"] = ""
        self.props["Font"] = "Arial"
        self.props["FontSize"] = 10
        
    def build(self, shapeDesc, data, factory, indent = 0):
        Drawer.build(self, shapeDesc, data, factory, indent)
        p = self.props
        self.text = str(p["Text"])
        if self.text == "":
            self.text = "."
        self.font = QFont(p["Font"], int(p["FontSize"]))
        
    def desiredSize(self):
        if not self.bestSize.isNull():
            result = self.bestSize
        else:
            fm = QFontMetrics(self.font)
            s = fm.size(Qt.TextSingleLine, self.text)
            result = asQSizeF(s)
            self.bestSize = result
        #print "T DS:", result.width(), result.height()
        return result

    def draw(self, pos, painter, indent = 0):
        painter.save()
        b = self._makeBrush()
        painter.setBrush(b)                   
        p = self._makePen()
        painter.setPen(p)
        size = self.desiredSize()
        painter.setFont(self.font)
        #print "T", " " * indent, pos.x(), pos.y() + size.height(), ":", self.text
        pos.setY(pos.y() + size.height())
        painter.drawText(pos, self.text)
        painter.restore()
        

class PictureDrawer(Drawer):
    def __init__(self, parent):
        Drawer.__init__(self, parent)

    def build(self, shapeDesc, data, factory, indent = 0):
        Drawer.build(self, shapeDesc, data, factory, indent)

    def desiredSize(self):
        if not self.bestSize.isNull():
            result = self.bestSize
        else:
            p = self._getPicture()
            result = QSizeF(p.size().width(), p.size().height())
            self.bstSize = result
        #print "P DS:", result.width(), result.height()
        return result

    def draw(self, pos, painter, indent = 0):
        #print "P", " " * indent, pos.x(), pos.y()
        p = self._getPicture()
        painter.drawPixmap(pos, p)

    def _getPicture(self):
        pixmap = QPixmap()
        pixmap.loadFromData(self.shapeDesc["Picture"], "PNG")         
        return pixmap

    
class ContainerDrawer(Drawer):
    def __init__(self, parent):
        Drawer.__init__(self, parent)
        self.subItems = []

    def build(self, shapeDesc, data, factory, indent = 0):
        Drawer.build(self, shapeDesc, data, factory, indent)
        if shapeDesc["Path"] == "":
            for subShapeDesc in shapeDesc.eachOutgoingClass("Shape.Contains.Shape"):
                newItem = factory.create(self, subShapeDesc)
                self.subItems.append(newItem)
                newItem.build(subShapeDesc, data, factory, indent + 2)
        else:
            for subShapeDesc in shapeDesc.eachOutgoingClass("Shape.Contains.Shape"):
                for newData in data.eachOutgoingClass(shapeDesc["Path"]):
                    newItem = factory.create(self, subShapeDesc)
                    self.subItems.append(newItem)
                    newItem.build(subShapeDesc, newData, factory, indent + 2)

    def desiredSize(self):
        if not self.bestSize.isNull():
            result = self.bestSize
        else:
            newH = 0.0
            newW = 0.0
            if self.shapeDesc["Layout"] == "Horizontal":
                for si in self.subItems:
                    subSize = si.desiredSize()
                    newW += subSize.width()
                    newH = max(newH, subSize.height())
            else:
                for si in self.subItems:
                    subSize = si.desiredSize()
                    newH += subSize.height()
                    newW = max(newW, subSize.width())
            result = QSizeF(newW, newH)
            self.bestSize = result
        return result

    def draw(self, pos, painter, indent = 0):
        painter.save()
        b = self._makeBrush()
        painter.setBrush(b)                   
        p = self._makePen()
        painter.setPen(p)
        r = QRectF()
        r.setTopLeft(pos)
        r.setSize(self.desiredSize())
        painter.drawRect(r)
        painter.restore()

        currentPos = QPointF(pos.x(), pos.y())
        newH = pos.y()
        newW = pos.x()
        #print " " * indent, "Curr", currentPos.x(), currentPos.y(), self.shapeDesc["Layout"]
        if self.shapeDesc["Layout"] == "Horizontal":
            for si in self.subItems:
                #print "DrawHorz:", currentPos.x(),  currentPos.y()
                si.draw(currentPos, painter, indent + 2)
                subSize = si.desiredSize()
                newW += subSize.width()
                currentPos.setX(newW)
                #print "Adding", newW, subSize.width()
            #print " " * indent, "EndHorz"
        else:
            #print "newH", newH
            for si in self.subItems:
                #print "DrawVert:", currentPos.x(),  currentPos.y()
                si.draw(currentPos, painter, indent + 2)
                subSize = si.desiredSize()
                newH += subSize.height()
                #print newH
                currentPos.setY(newH)
            #print " " * indent, "EndVert"

class Factory:
    def __init__(self):
        pass
    def create(self, parent, shapeDesc):
        newItem = None
        if shapeDesc["Type"] == "Text":
            newItem = TextDrawer(parent)
        elif shapeDesc["Type"] == "Picture":
            newItem = PictureDrawer(parent)
        elif shapeDesc["Type"] == "Container":
            newItem = ContainerDrawer(parent)
        else:
            assert(False)
        #newItem.setVisible(False)
        return newItem


class Node(QGraphicsPixmapItem):
    Type = QGraphicsItem.UserType + 1
    GlobalId = 0
    def __init__(self, parent = None):
        QGraphicsPixmapItem.__init__(self, parent)

        Node.factory = Factory()

        self.arrows = []
        self.setFlag(QGraphicsItem.ItemIsMovable, 1)
        self.setFlag(QGraphicsItem.ItemIsSelectable, 1)
        self.isDrawn = False
        self.newPos = QPointF()
        self.id = Node.GlobalId
        Node.GlobalId += 1
        
    def create(self, shapeDesc, data):
        assert(shapeDesc["Type"] == "Container")
        self.build(shapeDesc, data)

    def build(self, shapeDesc, data):
        self.shapeDesc = shapeDesc
        self.data = data

        self._updateNode()

    def _updateNode(self):
        # 1. Build structure
        containerDrawer = Node.factory.create(self, self.shapeDesc)
        containerDrawer.build(self.shapeDesc, self.data, Node.factory)

        # 2. Calculate sizes
        desiredSize = containerDrawer.desiredSize()
        
        # 3. Draw pixmap
        pixmap = QPixmap(QSize(desiredSize.width(), desiredSize.height()))

        painter = QPainter()
        pixmap.fill(Qt.white)
        painter.begin(pixmap)
        containerDrawer.draw(QPointF(0.0, 0.0), painter)
        painter.end()

        # 4. set pixmap
        self.setPixmap(pixmap)
        
        w = pixmap.size().width() / 2
        h = pixmap.size().height() / 2
        self.pixrect = QRectF(-w, -h, +w, +h)
        self.setOffset(-w, -h)

    def update(self):
        self._updateNode()

    def rect(self):
        return self.pixrect

    def removeArrow(self, arrow):
        try:
            index = self.arrows.index(arrow)
            del arrows[index]
        except ValueError:
            pass

    def removeArrows(self):
        for a in self.arrows:
            a.startItem().removeArrow(a)
            a.endItem().removeArrow(a)
            self.scene().removeItem(a)
            del a

    def addArrow(self, arrow):
        self.arrows.append(arrow)

    def itemChange(self, change, value):
        if change == QGraphicsItem.ItemPositionChange:
            for a in self.arrows:
                a.updatePosition()
        return value

    def type(self):
        return Node.Type

    def updateFromRep(self):
        x = self.data["x"]
        y = self.data["y"]

        self.setPos(QPointF(float(x),float(y)))



class Tool(QObject):
    NodeTool = 1
    LinkTool = 2
    MoveTool = 3
    NodeDrawTool = 3
    def __init__(self, typ, parent = None):
        QObject.__init__(self, parent)
        self.parent = parent
        self.toolType = typ

    def mousePressEvent(self, mouseEvent):
        pass
    def mouseMoveEvent(self, mouseEvent):
        pass
    def mouseReleaseEvent(self, mouseEvent):
        pass

class MoveTool(Tool):
    def __init__(self, parent = None):
        Tool.__init__(self, Tool.MoveTool, parent)

    def mouseMoveEvent(self, mouseEvent):
        # Overwrite diagram scenes mouse move event by super class
        QGraphicsScene.mouseMoveEvent(self.parent, mouseEvent)

    def mouseReleaseEvent(self, mouseEvent):
        node = self.parent.itemAt(mouseEvent.scenePos())
        if node:
            if node.type() == QGraphicsItem.UserType + 1:
                self.parent.emit(SIGNAL("nodeMoved"), node.data,
                                 node.pos())

class NodeTool(Tool):
    def __init__(self, toolDef, parent = None):
        Tool.__init__(self, Tool.NodeTool, parent)
        self.toolDef = toolDef

    def mousePressEvent(self, mouseEvent):
        #print "mouseEvent - NodeTool"
        self.parent.emit(SIGNAL("createNodeRequest"), mouseEvent.scenePos(), QPointF(), self.toolDef)

class NodeDrawTool(Tool):
    def __init__(self, toolDef, parent = None):
        Tool.__init__(self, Tool.NodeDrawTool, parent)
        self.toolDef = toolDef
        self.startPos = QPointF()
        self.myRect = None

    def mousePressEvent(self, mouseEvent):
        #print "mouseEvent - NodeTool"
        self.startPos = mouseEvent.scenePos()
        rect = QRectF()
        rect.setTopLeft(mouseEvent.scenePos())
        rect.setBottomRight(mouseEvent.scenePos())
        self.myRect = QGraphicsRectItem(rect)
        self.parent.addItem(self.myRect)

    def mouseMoveEvent(self, mouseEvent):
        if self.myRect != None:
            rect = self.myRect.rect()
            rect.setBottomRight(mouseEvent.scenePos())
            self.myRect.setRect(rect)

    def mouseReleaseEvent(self, mouseEvent):
        if self.myRect != None:
            #print "mouseReleaseEvent"
            self.parent.removeItem(self.myRect)
            del self.myRect
            self.myRect = None
            self.parent.emit(SIGNAL("createNodeRequest"), 
                             self.startPos, mouseEvent.scenePos(), self.toolDef)


class LinkTool(Tool):
    def __init__(self, toolDef, parent = None):
        Tool.__init__(self, Tool.LinkTool, parent)
        self.toolDef = toolDef
        self.myLine = None
        self.myLineColor = Qt.black

    def mousePressEvent(self, mouseEvent):
        self.myLine = QGraphicsLineItem(QLineF(mouseEvent.scenePos(),
                                               mouseEvent.scenePos()))
        self.myLine.setPen(QPen(self.myLineColor, 2))
        self.parent.addItem(self.myLine)

    def mouseMoveEvent(self, mouseEvent):
        if self.myLine != None:
            newLine = QLineF(self.myLine.line().p1(), mouseEvent.scenePos())
            self.myLine.setLine(newLine)

    def mouseReleaseEvent(self, mouseEvent):
        startItems = self.parent.items(self.myLine.line().p1())
        
        if len(startItems) > 0 and startItems[0] == self.myLine:
             del startItems[0]
        endItems = self.parent.items(self.myLine.line().p2())
        if len(endItems) > 0 and endItems[0] == self.myLine:
             del endItems[0]

        self.parent.removeItem(self.myLine)
        del self.myLine
        self.myLine = None

        if len(startItems) > 0 and len(endItems) > 0: 
            if startItems[0].type() == Node.Type:
                if endItems[0].type() == Node.Type:
                    if startItems[0] != endItems[0]:
                        ## Emit CreateEdgeRequest
                        startItem = startItems[0]
                        destItem = endItems[0]
                        self.parent.emit(SIGNAL("createEdgeRequest"), 
                                         startItem.data,
                                         destItem.data, self.toolDef)
        self.myLine = None

class DiagramScene(QGraphicsScene):
    InsertItem = 1
    InsertLine = 2
    MoveItem = 3

    def __init__(self, itemMenu, parent = None):
        QGraphicsScene.__init__(self, parent)
        self.myItemMenu = itemMenu
        self.myItemColor = Qt.black
        self.myMode = DiagramScene.MoveItem
        self.leftButtonDown = 0
        self.startPoint = QPointF()
        self.myItemColor = Qt.white
        self.setBackgroundBrush(Qt.lightGray)

        self.objectMap = {}    # Mapping between rep object id and scene item
        self.tool = MoveTool(self)

    def setTool(self, tool):
        self.tool = tool

    def editorLostFocus(self, item):
        pass

    def createEdge(self, repObject, edgeDef):
        if repObject.isClass():
            
            if edgeDef["StartAssoc"] in repObject.intention.outgoing:
                startObject = repObject.firstOutgoingClass(edgeDef["StartAssoc"])
            else:
                startObject = repObject.firstIncomingClass(edgeDef["StartAssoc"])

            if edgeDef["EndAssoc"] in repObject.intention.outgoing:
                endObject = repObject.firstOutgoingClass(edgeDef["EndAssoc"])
            else:
                endObject = repObject.firstIncomingClass(edgeDef["EndAssoc"])

            startItem = self.objectMap[startObject.identifierPath()]
            destItem = self.objectMap[endObject.identifierPath()]
        else:
            startItem = self.objectMap[repObject.source.identifierPath()]
            destItem = self.objectMap[repObject.target.identifierPath()]
        arrow = Edge(repObject, edgeDef, startItem, destItem)
        arrow.myColor = Qt.black
        startItem.addArrow(arrow)
        destItem.addArrow(arrow)
        arrow.setZValue(-1000.0)
        self.addItem(arrow)
        arrow.updatePosition()
        self.objectMap[repObject.identifierPath()] = arrow

    def createNode(self, repObject, nodeDef):
        #print "Createnode"
        item = Node()
        mainShapeDesc = nodeDef.firstOutgoingClass("NodeDef.VisualizedBy.Shape")
        item.create(mainShapeDesc, repObject)
        item.update()
        self.addItem(item)
        self.objectMap[repObject.identifierPath()] = item
        item.setPos(QPointF(float(repObject["x"]), float(repObject["y"])))

    def updateItem(self, repObject, key):
        if repObject.isClass():
            item = self.objectMap.get(repObject.identifierPath(), None)
            if item:
                item.update()
                if key in ["x", "y"]:
                    item.updateFromRep()

    def deleteItem(self, repObject):
        item = self.objectMap.get(repObject.identifierPath(), None)
        if item:
            self.removeItem(item)

    def mousePressEvent(self, mouseEvent):
        #print "MousePressEvent"
        if mouseEvent.button() != Qt.LeftButton:
            return
        self.tool.mousePressEvent(mouseEvent)
        QGraphicsScene.mousePressEvent(self, mouseEvent)

    def mouseMoveEvent(self, mouseEvent):
        self.tool.mouseMoveEvent(mouseEvent)

    def mouseReleaseEvent(self, mouseEvent):
        self.tool.mouseReleaseEvent(mouseEvent)
        QGraphicsScene.mouseReleaseEvent(self, mouseEvent)


class MainWindow(QMainWindow):
    def __init__(self, rData, rDiagram, parent = None):
        QMainWindow.__init__(self, parent)
        self.setWindowTitle("Graph")
        self.scene = None
        self.view = None
        self.exitAction = None
        self.addAction = None
        self.deleteAction = None
        self.startLayoutAction = None
        self.stopLayoutAction = None
        self.fileMenu = None
        self.itemMenu = None
        self.pointerToolbar = None
        self.sceneScaleCombo = None
        self.toolBox = None
        self.buttonGroup = None
        self.pointerTypeGroup = None
        self.backgroundButtonGroup = None
        self.editToolBar = None
        self.lineAction = None
        self.zoomAction025 = None
        self.zoomAction050 = None
        self.zoomAction100 = None
        self.zoomAction200 = None
        self.tools = {}
        self.currentToolId = 1
        self.zoomLevel = 1
        
        self.scene = DiagramScene(self.itemMenu)
        self.scene.setSceneRect(QRectF(-2500, -2500, 5000, 5000))

        self._createActions()
        self._createToolBox(rDiagram)
        self._createMenus()

        self.controller = Controller(rData, rDiagram, self)

        self._createToolbars() 
    
        layout = QHBoxLayout()
        layout.addWidget(self.toolBox)
        self.view = QGraphicsView(self.scene)
        layout.addWidget(self.view)
    
        widget = QWidget()
        widget.setLayout(layout)
    
        self.setCentralWidget(widget)
        self.setWindowTitle(rData["Name"] + " (" + rData["Type"] + ")")
        self.setWindowIcon(QIcon("img/spiderIco.png"))
 
    def buttonGroupClicked(self, id):
        buttons = self.buttonGroup.buttons()
        for button in buttons:
            if self.buttonGroup.button(id) != button:
                button.setChecked(0)

        self.scene.setTool(self.tools[id])

    def deleteItem(self):
        self.emit(SIGNAL("removeRequest"))

    def startLayout(self):
        self.layouter = ForcedGraphLayout(self.scene, self)
        self.layouter.layout()
    def stopLayout(self):
        self.layouter.stop()
        del self.layouter
        self.layouter = None
        
    def startTransaction(self):
        self.controller.repository.transaction.begin()

    def endTransaction(self):
        self.controller.repository.transaction.rollback()        

    def showMatrixBrowser(self):
        self.matrix = Ui.MatrixWidget(self.controller.repository)
        self.matrix.show()

    def showFastEdit(self):
        self.fastEdit = FastEdit.FastEditDlg(self.controller.repository, self)
        self.fastEdit.show()
        items = self.scene.selectedItems()
        if len(items) == 1:
            item = items[0]
            self.fastEdit.navigateRequest(item.data)

    def save(self):
        r = self.controller.repository
        exporter = Persistence.Exporter(r)
        exporter.save(r["FileName"])

    def load(self):
        fileName = QFileDialog.getOpenFileName(self, "Open diagram", ".", 
                   "Spider files (*.spid)")
        if fileName:
            importer = Persistence.Importer()
            fileName = str(fileName)
            r = importer.load(fileName)
            if r:
                r["FileName"] = fileName
                self.scene.objectMap.clear()
                self.controller.setData(r)

    def pointerGroupClicked(self, id):
        # Currently only move tool
        tool = self.tools[self.pointerTypeGroup.checkedId()]
        self.scene.setTool(tool)

    def sceneScaleChanged(self, scale):
        newScale = scale.left(scale.indexOf("%")).toDouble()[0] / 100.0
        oldMatrix = self.view.matrix()
        self.view.resetMatrix()
        self.view.translate(oldMatrix.dx(), oldMatrix.dy())
        self.view.scale(newScale, newScale)

    def itemSelected(self, item):
        pass

    def _createToolBox(self, rDiagram):
        self.buttonGroup = QButtonGroup()
        self.buttonGroup.setExclusive(0)
        self.connect(self.buttonGroup, SIGNAL("buttonClicked(int)"),
                     self.buttonGroupClicked)
        layout = QGridLayout()

        for toolDef in rDiagram.eachClass("Tool"):
            layout.addWidget(self._createCellWidget(toolDef), 
                             self.currentToolId, 0)
            self.currentToolId += 1

        layout.setRowStretch(self.currentToolId, 10)

        itemWidget = QWidget()
        itemWidget.setLayout(layout)

        self.toolBox = QToolBox()
        self.toolBox.setSizePolicy(QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Ignored))
        self.toolBox.setMinimumWidth(itemWidget.sizeHint().width())
        self.toolBox.addItem(itemWidget, "Stefans test")

    def _createActions(self):
        self.deleteAction = QAction("&Delete", self)
        self.connect(self.deleteAction, SIGNAL("triggered()"),
                     self.deleteItem)
        self.startLayoutAction = QAction("Start force &layout", self)
        self.connect(self.startLayoutAction, SIGNAL("triggered()"),
                     self.startLayout)
        self.stopLayoutAction = QAction("Stop force &layout", self)
        self.connect(self.stopLayoutAction, SIGNAL("triggered()"),
                     self.stopLayout)
        self.exitAction =  QAction("E&xit", self)
        self.connect(self.exitAction, SIGNAL("triggered()"), self.close)
        self.startTransactionAction =  QAction("Start transaction", self)
        self.connect(self.startTransactionAction, SIGNAL("triggered()"), 
                     self.startTransaction)
        self.endTransactionAction =  QAction("End transaction", self)
        self.connect(self.endTransactionAction, SIGNAL("triggered()"), 
                     self.endTransaction)
        self.matrixBrowserAction = QAction("MatrixBrowser", self)
        self.connect(self.matrixBrowserAction, SIGNAL("triggered()"), 
                     self.showMatrixBrowser)
        self.fastEditAction = QAction("FastEdit", self)
        self.connect(self.fastEditAction, SIGNAL("triggered()"), 
                     self.showFastEdit)
        self.saveAction = QAction("Save", self)
        self.connect(self.saveAction, SIGNAL("triggered()"), 
                     self.save)
        self.loadAction = QAction("Load", self)
        self.connect(self.loadAction, SIGNAL("triggered()"), 
                     self.load)
        self.zoomAction025 = QAction("Zoom x 1/4", self)
        self.connect(self.zoomAction025, SIGNAL("triggered()"),
                     self.zoomBy025) 
        self.zoomAction050 = QAction("Zoom x 1/2", self)
        self.connect(self.zoomAction050, SIGNAL("triggered()"),
                     self.zoomBy050)
        self.zoomAction100 = QAction("Zoom x 1", self)
        self.connect(self.zoomAction100, SIGNAL("triggered()"),
                     self.zoomBy100)
        self.zoomAction200 = QAction("Zoom x 2", self)
        self.connect(self.zoomAction200, SIGNAL("triggered()"),
                     self.zoomBy200)

    def zoomBy025(self):
        self.view.scale(0.25, 0.25)
    def zoomBy050(self):
        self.view.scale(0.50, 0.50)
    def zoomBy100(self):
        self.view.scale(1.00, 1.00)
    def zoomBy200(self):
        self.view.scale(2.00, 2.00)
        
    def _createMenus(self):
        self.fileMenu = self.menuBar().addMenu("&File")
        self.fileMenu.addAction(self.loadAction)
        self.fileMenu.addAction(self.saveAction)
        self.fileMenu.addAction(self.exitAction)
        self.itemMenu = self.menuBar().addMenu("&Item")
        self.itemMenu.addAction(self.deleteAction)
        self.itemMenu.addAction(self.startTransactionAction)
        self.itemMenu.addAction(self.endTransactionAction)
        self.itemMenu.addAction(self.matrixBrowserAction)
        self.itemMenu.addAction(self.fastEditAction)
        self.itemMenu = self.menuBar().addMenu("&Layout")
        self.itemMenu.addAction(self.startLayoutAction)
        self.itemMenu.addAction(self.stopLayoutAction)
        self.itemMenu = self.menuBar().addMenu("&View")
        self.itemMenu.addAction(self.zoomAction025)
        self.itemMenu.addAction(self.zoomAction050)
        self.itemMenu.addAction(self.zoomAction100)
        self.itemMenu.addAction(self.zoomAction200)


    def _createToolbars(self):
        self.editToolBar = self.addToolBar("Edit")
        self.editToolBar.addAction(self.deleteAction)

        pointerButton = QToolButton()
        pointerButton.setCheckable(1)
        pointerButton.setChecked(1)
        pointerButton.setIcon(QIcon("img/pointer.png"))

        self.tools[self.currentToolId] = MoveTool(self.scene)
        self.pointerTypeGroup = QButtonGroup()
        self.pointerTypeGroup.addButton(pointerButton, self.currentToolId)
        self.connect(self.pointerTypeGroup, SIGNAL("buttonClicked(int)"),
                     self.pointerGroupClicked)
        self.currentToolId += 1

        self.sceneScaleCombo = QComboBox()
        self.sceneScaleCombo.addItem("50%")
        self.sceneScaleCombo.addItem("75%")
        self.sceneScaleCombo.addItem("100%")
        self.sceneScaleCombo.addItem("125%")
        self.sceneScaleCombo.addItem("150%")
        self.sceneScaleCombo.setCurrentIndex(2)
        self.connect(self.sceneScaleCombo, 
                     SIGNAL("currentIndexChanged(const QString &)"),
                     self.sceneScaleChanged)

        self.pointerToolbar = self.addToolBar("Pointer type")
        self.pointerToolbar.addWidget(pointerButton)
        #self.pointerToolbar.addWidget(linePointerButton)
        self.pointerToolbar.addWidget(self.sceneScaleCombo)

    def _createCellWidget(self, toolDef):
        if toolDef["Type"] == "Node":
            tool = NodeTool(toolDef, self.scene)
        elif toolDef["Type"] == "DrawNode":
            tool = NodeDrawTool(toolDef, self.scene)
        else:
            tool = LinkTool(toolDef, self.scene)
        self.tools[self.currentToolId] = tool
        iconPic = QPixmap()
        iconPic.loadFromData(toolDef["Icon"], "PNG")
        icon = QIcon(iconPic)
    
        button = QToolButton()
        button.setIcon(icon)
        button.setIconSize(QSize(50, 50))
        button.setCheckable(1)
        self.buttonGroup.addButton(button, self.currentToolId)
    
        layout = QGridLayout()
        layout.addWidget(button, 0, 0, Qt.AlignHCenter)
        #print toolDef
        layout.addWidget(QLabel(toolDef["Name"]), 1, 0, Qt.AlignCenter)
    
        widget = QWidget()
        widget.setLayout(layout)
        return widget


class Controller(QObject):
    # List of commands!
    def __init__(self, rData, rDiagram, parent=None):
        QObject.__init__(self, parent)
        self.scene = parent.scene
        self.entityDefs = {}
        self._setupModels(rData, rDiagram)
        self.connect(parent, SIGNAL("removeRequest"),
                     self.view_removeRequest)

    def setData(self, rData):
        self.repository = rData
        assert(rData.isMetaModel())
        self.root = None
        diagram = getFirst(self.diagramDef.metaClasses["Diagram"].extension)
        diagramRoot = diagram["Root"]
        if diagramRoot != "":
            self.root = getFirst(rData.metaClasses[diagramRoot].extension)
            if not self.root:
                self.root = rData.Class(diagramRoot)

        # Currently we need to store the positions somewhere
        # create them on the fly
        for nodeDef in self.diagramDef.eachClass("NodeDef"):
            mcName = nodeDef["MetaEntity"]
            mc = rData.getMetaEntity(mcName)
            if not "x" in mc.metaProperties:
                rData.MetaProperty(mcName, "x", "Float", "x")
            if not "y" in mc.metaProperties:
                rData.MetaProperty(mcName, "y", "Float", "y")
            if not "h" in mc.metaProperties:
                rData.MetaProperty(mcName, "h", "Float", "h")
            if not "w" in mc.metaProperties:
                rData.MetaProperty(mcName, "w", "Float", "w")

        if not self.repository.watcher:
            # we need a watcher 
            assert(0)
            self.repository.watcher = Watcher()

        # connect gui events and model changes to us
        emitter = self.repository.watcher.addListener(Ui.Emitter(), "Emitter")
        self.connect(emitter, SIGNAL("propertyChanged"), 
                           self.model_propertyChanged)
        self.connect(emitter, SIGNAL("entityCreated"), 
                           self.model_entityCreated)
        self.connect(emitter, SIGNAL("entityDeleted"), 
                           self.model_entityDeleted)
        self.connect(self.scene, SIGNAL("createNodeRequest"),
                           self.view_createNodeRequest)
        self.connect(self.scene, SIGNAL("createEdgeRequest"),
                           self.view_createEdgeRequest)
        self.connect(self.scene, SIGNAL("nodeMoved"),
                           self.view_nodeMoved)
       
        for nodeDef in self.diagramDef.eachClass("NodeDef"):
            mcName = nodeDef["MetaEntity"]
            for o in self.repository.eachClass(mcName):
                self.repository.watcher.entityCreated(o)
        for edgeDef in self.diagramDef.eachClass("EdgeDef"):
            maName = edgeDef["MetaEntity"]
            if self.repository.isAssocName(maName):
                for o in self.repository.eachAssoc(maName):
                    self.repository.watcher.entityCreated(o)
            else:
                for o in self.repository.eachClass(maName):
                    self.repository.watcher.entityCreated(o)


    def _setupModels(self, rData, rDiagram):
        self.diagramDef = rDiagram
        assert(rDiagram.isMetaModel())

        # Make a mapping between meta entity name and display definition
        for nodeDef in rDiagram.eachClass("NodeDef"):
            self.entityDefs[nodeDef["MetaEntity"]] = nodeDef
        for edgeDef in rDiagram.eachClass("EdgeDef"):
            self.entityDefs[edgeDef["MetaEntity"]] = edgeDef

        self.setData(rData)


    def view_createEdgeRequest(self, startObject, destObject, toolDef):
        #print "view_createEdgeRequest"
        metaObjectName = toolDef["MetaEntity"]
        metaEntity = self.repository.getMetaEntity(metaObjectName)
        if toolDef["Type"] == "Edge":
            assoc = self.repository.Assoc(startObject, destObject, metaObjectName)
        else:
            # A NodeEdge is an edge 
            assert(toolDef["Type"] == "NodeEdge")
            cls = self.repository.Class(metaObjectName)
            newX = (startObject["x"] + destObject["x"]) / 2
            newY = (startObject["y"] + destObject["y"]) / 2
            cls["x"] = newX
            cls["y"] = newY
            cls["w"] = 100
            cls["h"] = 80

            self._connectClassToRoot(cls)
            sourceAssocName = toolDef["Source"]
            sourceMetaAssoc = self.repository.metaAssocs[sourceAssocName]
            if sourceMetaAssoc.source == cls.intention:
                assoc1 = self.repository.Assoc(cls, startObject, sourceAssocName)
            else:
                assoc1 = self.repository.Assoc(startObject, cls, sourceAssocName)


            targetAssocName = toolDef["Target"]
            targetMetaAssoc = self.repository.metaAssocs[targetAssocName]
            if targetMetaAssoc.source == cls.intention:
                assoc2 = self.repository.Assoc(cls, destObject, targetAssocName)
            else:
                assoc2 = self.repository.Assoc(destObject, cls, targetAssocName)

    def view_createNodeRequest(self, pos1, pos2, toolDef):
        print "view_createNodeRequest"
        assert(toolDef["Type"] == "Node" or toolDef["Type"] == "DrawNode")
        mcName = toolDef["MetaEntity"]
        cls = self.repository.Class(mcName)
        if pos2 == QPointF():
            cls["x"] = pos1.x()
            cls["y"] = pos1.y()
            w = 100           
            h = 80
        else:
            rect = QRectF()
            rect.setTopLeft(pos1)
            rect.setBottomRight(pos2)
            w = rect.width()
            h = rect.height()
            cls["x"] = pos1.x() + w / 2
            cls["y"] = pos1.y() + h / 2
        cls["w"] = w
        cls["h"] = h

        self._connectClassToRoot(cls)

    def view_nodeMoved(self, repObject, pos):
        repObject["x"] = pos.x()
        repObject["y"] = pos.y()
        
    def view_cloneRequest(self):
        pass
    def view_navigateRequest(self):
        pass
    def view_removeRequest(self):
        for item in self.scene.selectedItems():
            if item.data.isAssoc():
                self.repository.deleteAssoc(item.data)
        for item in self.scene.selectedItems():
            if item.data.isClass():
                self.repository.deleteClass(item.data)

    def model_propertyChanged(self, repObject, key, oldValue, newValue):
        self.scene.updateItem(repObject, key)

    def model_entityCreated(self, repObject):
        if repObject.isExtensionEntity():
            metaEntity = repObject.intention["Name"]
            if metaEntity in self.entityDefs:
                # not all meta entities are displayed
                displayDef = self.entityDefs[metaEntity]
                #sn: Change for new edgedef: if repObject.isAssoc():
                if displayDef.intention["Name"] == "EdgeDef":
                    self.scene.createEdge(repObject, displayDef)
                else:
                    self.scene.createNode(repObject, displayDef)

    def model_entityDeleted(self, repObject):
        self.scene.deleteItem(repObject)

    def _connectClassToRoot(self, cls):
        # Connect a newly created class to the root, if necessary
        #print "_connectClassToRoot"
        if self.root:
            mc = cls.intention
            for ma in mc.eachIncomingMetaAssoc():
                if ma.source == self.root.intention and ma["Aggregation"]:
                    self.repository.Assoc(self.root, cls, ma["Name"])


class Graph(Component.Component):
    def __init__(self):
        Component.Component.__init__(self)

    def interface(self, interfaceName):
        if interfaceName == "Spider.IProject":
            return self
        elif interfaceName == "Spider.IManagedComponent":
            return self
        return None

    # Interface Spider.IManagedComponent
    @staticmethod
    def setupComponentType(rSpiderCOMP):
        return Component.Component._setupComponentType(rSpiderCOMP, "Spider.Graph.Graph",
                                                       "Supports a UI for creating diagrams",
                                                       1, ["Spider.IProject", "Spider.IManagedComponent"])

    # Interface Spider.IProject
    def open(self, project, r, item):
        self.project = project
        self.projectItem = item

        diagramRep = project.project.loadDiagramDef(r["Type"])
        self.mw = MainWindow(r, diagramRep, project)
        self.mw.setGeometry(100, 100, 500, 500)
        self.mw.show()


class ForcedGraphLayout(QWidget):
    def __init__(self, scene, parent = None):
        QWidget.__init__(self, parent)
        self.scene = scene
        self.timerId = 0

    def layout(self):
        for item in self.scene.items():
            if isinstance(item, Node):
                item.setPos(item.pos() + QPointF(random.randint(-100, 100),
                                                 random.randint(-100, 100)))
                item.newPos = item.pos()
        self.timerId = self.startTimer(1000 / 25)

    def stop(self):
        print "stopped"
        self.killTimer(self.timerId)
        self.timerId = 0
        for item in self.scene.items():
            if isinstance(item, Node):
                x = item.pos().x()
                y = item.pos().y() 
                item.data["x"] = x
                item.data["y"] = y

        
    def calculateForces(self, currentNode):
        # Sum up all forces pushing this item away.
        xvel = 0.0
        yvel = 0.0
        colliding = False
        for item in self.scene.items():
            if not isinstance(item, Node):
                continue

            line = QLineF(currentNode.mapFromItem(item, 0, 0), QPointF(0, 0))
            dx = line.dx()
            dy = line.dy()
            l = 2.0 * (dx * dx + dy * dy)

            if currentNode.collidesWithItem(item):
                dx *= 20
                dy *= 20
                colliding = True
                
            if l > 0:
                xvel += (dx * 150.0) / l
                yvel += (dy * 150.0) / l

        # Now subtract all forces pulling items together.
        weight = (len(currentNode.arrows) + 1) * 10.0
        if colliding:
            weight *= 20
        for edge in currentNode.arrows:
            if edge.myStartItem is currentNode:
                pos = currentNode.mapFromItem(edge.myDestItem, 0, 0)
            else:
                pos = currentNode.mapFromItem(edge.myStartItem, 0, 0)
            xvel += pos.x() / weight
            yvel += pos.y() / weight
    
        if qAbs(xvel) < 0.3 and qAbs(yvel) < 0.3:
            xvel = yvel = 0.0

        if colliding:
            if len(currentNode.arrows) == 0:
                xvel = yvel = 0.0
                
        sceneRect = self.scene.sceneRect()
        currentNode.newPos = currentNode.pos() + QPointF(xvel, yvel)
        currentNode.newPos.setX(min(max(currentNode.newPos.x(), sceneRect.left() + 10),
                                    sceneRect.right() - 10))
        currentNode.newPos.setY(min(max(currentNode.newPos.y(), sceneRect.top() + 10),
                                    sceneRect.bottom() - 10))

    def advance(self, currentNode):
        if currentNode.newPos == currentNode.pos():
            return False

        currentNode.setPos(currentNode.newPos)
        return True

    def timerEvent(self, event):
        nodes = [item for item in self.scene.items() if isinstance(item, Node)]

        #print "timerEvent" + str(random.randint(-2000, 2000))
        for node in nodes:
            self.calculateForces(node)

        itemsMoved = False
        for node in nodes:
            if self.advance(node):
                itemsMoved = True
                
        if not itemsMoved:
            print "killTimer"
            self.killTimer(self.timerId)
            self.timerId = 0

def _random(rData):
    rData.MetaProperty("MetaClass", "x", "Float", "x")
    rData.MetaProperty("MetaClass", "y", "Float", "y")
    rData.MetaProperty("MetaAssoc", "x", "Float", "x")
    rData.MetaProperty("MetaAssoc", "y", "Float", "y")
    mm = rData.Class("MetaModel")
    classes = {}
    mp = rData.find("MetaType", "Name", "Integer")
    for i in range(0,201):
       
       cls = rData.Class("MetaClass") 
       classes[i] = cls
       cls["Name"] = "C" + str(i)
       cls["x"] = random.randint(-2000, 2000)
       cls["y"] = random.randint(-2000, 2000)
       rData.Assoc(mm, cls, "Contains")
       for l in range(0, random.randint(1,30)):
           p = rData.Class("MetaProperty")
           p["Name"] = "Prop" + str(i) + "_" + str(l) + str(random.randint(0,2000))
           rData.Assoc(cls, p, "Has")

    for i in range(0,201):
       c1 = random.randint(0,200)
       c2 = random.randint(0,200)
       cls1 = classes[c1]
       cls2 = classes[c2]
       ma = rData.Class("MetaAssoc")
       ma["x"] = (cls1["x"] + cls2["x"]) / 2
       ma["y"] = (cls1["y"] + cls2["y"]) / 2
       rData.Assoc(ma, cls1, "Source")
       rData.Assoc(ma, cls2, "Target")
       rData.Assoc(mm, ma, "Contains")

def createMmmRep():
    rDiagram = buildMetaModel("DiagramDef", "SpiderDIAGRAM")
    diagram = rDiagram.Class("Diagram")
    diagram["Name"] = "MMM Definition"
    diagram["Root"] = "MetaModel"

    # These meta entities are displayed as node
    # ---------- MetaClass ----------

    nodeDef = rDiagram.Class("NodeDef")
    nodeDef["Name"] = "MetaClass"
    nodeDef["MetaEntity"] = "MetaClass"


    cmain = rDiagram.Class("Shape")
    cmain["Type"] = "Container"  # Hauptcontainer: MetaClass
    cmain["Path"] = ""           # take object from parent: MetaClass, must here be ""
    cmain["Layout"] = "Vertical"
    cmain["BackgroundColor"] = (255,255,0,255)
    cmain["BorderColor"] = (0,0,0,255)
    rDiagram.Assoc(nodeDef, cmain, "VisualizedBy")

    sheader = rDiagram.Class("Shape")
    sheader["Type"] = "Text"
    sheader["Path"] = ""        # take object from parent: MetaClass
    sheader["BackgroundColor"] = (0,255,0,255)
    sheader["ForegroundColor"] = (0,0,0,255)
    sheader["BorderColor"] = (0,0,0,0)
    rDiagram.Assoc(cmain, sheader, "Contains") # Header

    cpropertysection = rDiagram.Class("Shape")
    cpropertysection["Type"] = "Container"    # Property Section
    cpropertysection["Path"] = "MetaClass.Has.MetaProperty"
    cpropertysection["Layout"] = "Vertical"
    cpropertysection["Alignment"] = "Top"
    cpropertysection["BackgroundColor"] = (0,255,255,255)
    cpropertysection["BorderColor"] = (0,0,0,0)
    rDiagram.Assoc(cmain, cpropertysection, "Contains")

    sproperty = rDiagram.Class("Shape")              # One property
    sproperty["Type"] = "Text"
    sproperty["BackgroundColor"] = (0,0,255,255)
    sproperty["BorderColor"] = (0,0,0,0)
    sproperty["ForegroundColor"] = (0,0,0,255)
    rDiagram.Assoc(cpropertysection, sproperty, "Contains")

    mapProperty = rDiagram.Class("Mapping")
    mapProperty["Property"] = "Text"
    mapProperty["Expression"] = "data['Name'] + ': ' + data.firstOutgoingClass('MetaProperty.Has.MetaType')['Name']"
    rDiagram.Assoc(sproperty, mapProperty, "FilledBy")
    
    mapHeader = rDiagram.Class("Mapping")
    mapHeader["Property"] = "Text"
    mapHeader["Expression"] = "'<<' + data['Name'] + '>>'"   # Just:PropertyName
    rDiagram.Assoc(sheader, mapHeader, "FilledBy")

    if 0:
        # Display metaassocs as links
        # ---------- Edges ----------
        edgeDef = rDiagram.Class("EdgeDef")
        edgeDef["Name"] = "AssocLink"
        edgeDef["MetaEntity"] = "MetaAssoc"
        edgeDef["StartAssoc"] = "MetaAssoc.Source.MetaClass"
        edgeDef["EndAssoc"] = "MetaAssoc.Target.MetaClass"

    else:
        # Display meta assocs as own node
        # ---------- MetaAssoc ----------
        nodeDef = rDiagram.Class("NodeDef")
        nodeDef["Name"] = "MetaAssoc"
        nodeDef["MetaEntity"] = "MetaAssoc"
        
        cmain2 = rDiagram.Class("Shape")
        cmain2["Type"] = "Container"
        cmain2["Path"] = ""
        cmain2["Layout"] = "Vertical"
        cmain2["BackgroundColor"] = (255,128,128,255)
        cmain2["BorderColor"] = (0,0,0,255)
        rDiagram.Assoc(nodeDef, cmain2, "VisualizedBy")
        
        sheader2 = rDiagram.Class("Shape")
        sheader2["Type"] = "Text"
        sheader2["Path"] = ""        # take object from parent: MetaClass
        sheader2["BackgroundColor"] = (0,255,0,255)
        sheader2["ForegroundColor"] = (0,0,0,255)
        sheader2["BorderColor"] = (0,0,0,0)
        rDiagram.Assoc(cmain2, sheader2, "Contains") # Header
        
        mapHeader2 = rDiagram.Class("Mapping")
        mapHeader2["Property"] = "Text"
        mapHeader2["Expression"] = "'<<' + data['Name'] + '>>'"   # Just:PropertyName
        rDiagram.Assoc(sheader2, mapHeader2, "FilledBy")
    
        #cpropertysection2 = rDiagram.Class("Shape")
        #cpropertysection2["Type"] = "Container"    # Property Section
        #cpropertysection2["Path"] = "MetaAssoc.Has.MetaProperty"
        #cpropertysection2["Layout"] = "Vertical"
        #cpropertysection2["Alignment"] = "Top"
        #cpropertysection2["BackgroundColor"] = (0,255,255,255)
        #cpropertysection2["BorderColor"] = (0,0,0,0)
        #rDiagram.Assoc(cmain2, cpropertysection2, "Contains")
    
        #sproperty2 = rDiagram.Class("Shape")              # One property
        #sproperty2["Type"] = "Text"
        #sproperty2["BackgroundColor"] = (0,0,255,255)
        #sproperty2["BorderColor"] = (0,0,0,0)
        #sproperty2["ForegroundColor"] = (0,0,0,255)
        #rDiagram.Assoc(cpropertysection2, sproperty2, "Contains")
    
        #mapProperty2 = rDiagram.Class("Mapping")
        #mapProperty2["Property"] = "Text"
        #mapProperty2["Expression"] = "data['Name'] + ': ' + data.firstOutgoingClass('MetaProperty.Has.MetaType')['Name']"
        #rDiagram.Assoc(sproperty2, mapProperty2, "FilledBy")

        # ---------- Edges ----------
        edgeDef = rDiagram.Class("EdgeDef")
        edgeDef["Name"] = "Source"
        edgeDef["MetaEntity"] = "MetaAssoc.Source.MetaClass"
        edgeDef = rDiagram.Class("EdgeDef")
        edgeDef["Name"] = "Target"
        edgeDef["MetaEntity"] = "MetaAssoc.Target.MetaClass"

    # ---------- Tools ----------
    # Here the different edit tools are defined
    tool = rDiagram.Class("Tool")
    tool["Type"] = "Node"
    tool["Name"] = "MetaClass"
    tool["MetaEntity"] = "MetaClass"
    tool["Icon"] = pixmapLoader("imgMMM/metaClass.png")

    tool = rDiagram.Class("Tool")
    tool["Type"] = "NodeEdge"
    tool["Name"] = "MetaAssoc"
    tool["MetaEntity"] = "MetaAssoc"
    tool["Source"] = "MetaAssoc.Source.MetaClass"
    tool["Target"] = "MetaAssoc.Target.MetaClass"
    tool["Icon"] = pixmapLoader("imgMMM/metaAssoc.png")



    #rData = buildMetaModel("MMM", "SpiderMMM", Watcher())
    #rData = buildMetaModel("MMM", "SpiderDIAGRAM", Watcher())
    rData = buildMetaModel("MMM", "SpiderSQL", Watcher())

    mmc = Component.MetaModelConverter()
    rData = mmc.extract(rData)
    rData["FileName"] = "SpiderMMM.spid"

    return (rDiagram, rData)

def createSqlRep():
    rDiagram = buildMetaModel("DiagramDef", "SpiderDIAGRAM")

    diagram = rDiagram.Class("Diagram")
    diagram["Name"] = "SQL Definition"
    diagram["Root"] = "Schema"

    # These meta entities are displayed as node
    nodeDef = rDiagram.Class("NodeDef")
    nodeDef["Name"] = "Table Node"
    nodeDef["MetaEntity"] = "Table"

    shTable = rDiagram.Class("Shape")
    shTable["Name"] = "Container:Table"
    shTable["Type"] = "Container"  # Hauptcontainer: MetaClass
    shTable["Path"] = ""           # take object from parent: MetaClass, must here be ""
    shTable["Layout"] = "Vertical"
    shTable["BackgroundColor"] = (255,255,0,255)
    shTable["BorderColor"] = (0,0,0,255)
    a = rDiagram.Assoc(nodeDef, shTable, "VisualizedBy")

    shTableHeader = rDiagram.Class("Shape")
    shTableHeader["Name"] = "TableHeader"
    shTableHeader["Type"] = "Text"
    shTableHeader["Path"] = ""        # take object from parent: MetaClass
    shTableHeader["BackgroundColor"] = (0,255,0,255)
    shTableHeader["ForegroundColor"] = (0,0,0,255)
    shTableHeader["BorderColor"] = (0,0,0,0)
    a = rDiagram.Assoc(shTable, shTableHeader, "Contains") # Header

    mapHeader = rDiagram.Class("Mapping")
    mapHeader["Property"] = "Text"
    mapHeader["Expression"] = "data['Name']"   # Just:PropertyName
    rDiagram.Assoc(shTableHeader, mapHeader, "FilledBy")

    cpropertysection = rDiagram.Class("Shape")
    cpropertysection["Name"] = "Container:Fields"
    cpropertysection["Type"] = "Container"    # Property Section
    cpropertysection["Path"] = "Table.Contains.Field"
    cpropertysection["Layout"] = "Vertical"
    cpropertysection["Alignment"] = "Top"
    cpropertysection["BackgroundColor"] = (128,128,128,255)
    cpropertysection["BorderColor"] = (0,0,0,0)
    a = rDiagram.Assoc(shTable, cpropertysection, "Contains")

    sproperty = rDiagram.Class("Shape")              # One property
    sproperty["Name"] = "Property"
    sproperty["Type"] = "Text"
    sproperty["BackgroundColor"] = (0,0,255,255)
    sproperty["BorderColor"] = (0,0,0,0)
    sproperty["ForegroundColor"] = (0,0,0,255)
    rDiagram.Assoc(cpropertysection, sproperty, "Contains")

    mapProperty = rDiagram.Class("Mapping")
    mapProperty["Property"] = "Text"
    mapProperty["Expression"] = "data['Name']"
    rDiagram.Assoc(sproperty, mapProperty, "FilledBy")


    nodeDef = rDiagram.Class("NodeDef")
    nodeDef["Name"] = "ForeignKey Node"
    nodeDef["MetaEntity"] = "ForeignKey"
    nodeDef["Icon"] = "img/edgeForeignKey.png"

    shFk = rDiagram.Class("Shape")
    shFk["Type"] = "Container"  # Hauptcontainer: MetaClass
    shFk["Path"] = ""           # take object from parent: MetaClass, must here be ""
    shFk["Layout"] = "Vertical"
    shFk["BackgroundColor"] = (100,100,255,255)
    shFk["BorderColor"] = (0,0,0,255)
    rDiagram.Assoc(nodeDef, shFk, "VisualizedBy")

    shfkHeader = rDiagram.Class("Shape")
    shfkHeader["Type"] = "Text"
    shfkHeader["Path"] = ""        # take object from parent: MetaClass
    shfkHeader["BackgroundColor"] = (0,255,0,255)
    shfkHeader["ForegroundColor"] = (0,0,0,255)
    shfkHeader["BorderColor"] = (0,0,0,0)
    rDiagram.Assoc(shFk, shfkHeader, "Contains") # Header

    mapHeader = rDiagram.Class("Mapping")
    mapHeader["Property"] = "Text"
    mapHeader["Expression"] = "data['Name']"   # Just:PropertyName
    rDiagram.Assoc(shfkHeader, mapHeader, "FilledBy")

    # These meta entities are displayed as edge
    edgeDef = rDiagram.Class("EdgeDef")
    edgeDef["Name"] = "ForeignKey"
    edgeDef["MetaEntity"] = "ForeignKey"
    edgeDef["StartAssoc"] = "Table.Has.ForeignKey"
    edgeDef["EndAssoc"] = "ForeignKey.RefersTo.Table"
    #edgeDef = rDiagram.Class("EdgeDef")
    #edgeDef["Name"] = "Reference"
    #edgeDef["MetaEntity"] = "ForeignKey.RefersTo.Table"
    
    # Here the different edit tools are defined
    tool = rDiagram.Class("Tool")
    tool["Type"] = "Node"
    tool["Name"] = "Table"
    tool["MetaEntity"] = "Table"
    tool["Icon"] = "img/table.png"

    tool = rDiagram.Class("Tool")
    tool["Type"] = "NodeEdge"
    tool["Name"] = "ForeignKey"
    tool["MetaEntity"] = "ForeignKey"
    tool["Source"] = "Table.Has.ForeignKey"
    tool["Target"] = "ForeignKey.RefersTo.Table"
    tool["Icon"] = "img/edgeForeignKey.png"

    im = Persistence.Importer()
    filename = "Data/My2/Test/MySql.spid"
    r = im.load(filename)
    r.watcher.addListener(Ui.Emitter(), "Emitter")
    r["FileName"] = "SpiderSQL.spid"
    return (rDiagram, r)



if __name__ == "__main__":
    app = QApplication(sys.argv)

    #(rDiagram, rData) = createSqlRep()
    (rDiagram, rData) = createMmmRep()
    
    mw = MainWindow(rData, rDiagram)
    mw.setGeometry(100, 100, 500, 500)
    mw.show()

    sys.exit(app.exec_())


"""
Model:NodeCreated, EdgeCreated, NodeChanged, EdgeChanged
"""
